// *************************************
//
// Description: Pooling allocator class
// $Id: Pool.h 13909 2008-02-03 22:02:23Z jcasper $
//
// **************************************
#ifndef __POOL_H__
#define __POOL_H__

#include <typeinfo>
#include <sys/types.h>

#include "base/simtypes.h"
#include "util/SimSupers.h"

// TODO add config option for what malloc version to use
#if 1
// Use Doug Lea's malloc version 2.8.3
// http://gee.cs.oswego.edu/dl/
#define MALLOC dlmalloc
#define CALLOC dlcalloc
#define MEMALIGN dlmemalign
#define FREE dlfree
#include "malloc.h"
#else 
// Use DJ Delorie's malloc6.c
// http://www.delorie.com/djgpp/malloc/
#define MALLOC test_malloc
#define CALLOC test_calloc
#define FREE test_free
#include "malloc6.h"
#endif

#define NEW new (__FILE__, __LINE__)

#define WARNING_MSG_STRIDE 10000

class Logger;

class Pool {
 public:
    // overloading new and delete operators to force use of CALLOC/FREE
    void* operator new (size_t size, const char* file, int line);
    void operator delete (void* mem, size_t size);
    void* operator new (size_t size, void* ptr, const char* file, int line);
    void* operator new[] (size_t size, const char* file, int line);
    void operator delete[] (void* mem, size_t size);

    Pool (const char*  _name,
          size_t       _size,
          int          _max,
          size_t       _padding,
          bool         _large);

    ~Pool ();

    void* calloc (size_t s, const char* file, int line);

    void free (void* mem, size_t size);
    
    // this dumps pool statistics to instructionOutput
    static void cleanupPools ();
    void startTracing();
    void stopTracing();

    static const uint CACHE_LINE_PADDING = 32;
    static void set_shutdown_asserts (bool b) {
        shutdown_asserts = b;
    }

 private:
    static bool initializedClass;
    static void initializeClass ();
    
    static bool shutdown_asserts;

    static const uint MAX_POOLS = 512;
    static Pool* Pools[MAX_POOLS];
    static uint PoolIndex;
    
    // used by cleanupPools for dumping information at end of program
    static Logger* PoolLog;
    // dump Pool statistics
    void log ();
    
    // human readable name
    const char* name;

    // expected size of objects
    size_t size;
    
    // extra size, typically used for cache line padding
    size_t padding;
    
    // allow larger than size requests for STL pool_allocator
    bool large;

    // free list
    void* freePtr;
    uint  freeCount;
    uint  freeMax;
    void  freePush(void*);
    void* freePop();

    // dealloc always frees memory, as opposed to free which can recycle memory
    void  dealloc(void*);

    // statistics
    uint64 total_new;
    uint64 total_delete;
    uint64 currently_allocated;
    uint64 peak_allocated;
    uint64 total_calloc;
    uint64 total_free;
    uint64 total_recycled;
    uint64 total_calloc_large;
    uint64 total_free_large;

    // for tracing
    bool tracing;

    // this is to monitor the allocation peak before simulator dies due to malloc failure
    uint nextWarningLevel;
};

/* 
   overloading new and delete operators to force use of CALLOC/FREE
   
   Use NEW_DELETE_OPERATORS_H(Pool,NoSuper) and NEW_DELETE_OPERATORS_CPP for classes
   defined with the usual .h .cpp split.

   Use NEW_DELETE_OPERATORS_INLINE_H and
   NEW_DELETE_OPERATORS_INLINE_CPP for inline classes. Because there
   is now a static pool data member, there has to at least be a single
   .cpp file to declare and initalized the pool data member. By
   convention we just place these in various *pools.cpp files.
*/

#define __quote__(x...) x  // needed for templates with a comma in them
#define NEW_DELETE_OPERATORS_H(ClassName,SuperClass)                    \
    static Pool* pool;                                                  \
    void* operator new (size_t size, const char* file, int line);       \
    void operator delete (void* mem, size_t size);                      \
    void* operator new (size_t size, void* ptr, const char* file, int line); \
    void* operator new[] (size_t size, const char* file, int line);     \
    void operator delete[] (void* mem, size_t size_t);                  \
    SET_SUPER_H(ClassName,__quote__(SuperClass))                        \
    
#define NEW_DELETE_OPERATORS_CPP(ClassName,max)                         \
    Pool* ClassName::pool = NEW Pool(#ClassName,                        \
                                     sizeof(ClassName),                 \
                                     max,                               \
                                     0,                                 \
                                     false);                            \
    void* ClassName::operator new (size_t size, const char* file, int line) \
    {                                                                   \
        return pool->calloc(size, file, line);                          \
    }                                                                   \
    void ClassName::operator delete (void* mem, size_t size)            \
    {                                                                   \
        pool->free(mem, size);                                          \
    }                                                                   \
    void* ClassName::operator new (size_t size, void* ptr, const char* file, int line) \
    {                                                                   \
        return ptr;                                                     \
    }                                                                   \
    void* ClassName::operator new[] (size_t size, const char* file, int line) \
    {                                                                   \
        return CALLOC(1, size);                                         \
    }                                                                   \
    void ClassName::operator delete[] (void* mem, size_t size_t)        \
    {                                                                   \
        FREE(mem);                                                      \
    }                                                                   \
    SET_SUPER_CPP(__quote__(ClassName))                                 \

#define NEW_DELETE_OPERATORS_INLINE_H(ClassName,SuperClass)             \
    static Pool* pool;                                                  \
    void* operator new (size_t size, const char* file, int line)        \
    {                                                                   \
        return pool->calloc(size, file, line);                          \
    }                                                                   \
    void operator delete (void* mem, size_t size_t)                     \
    {                                                                   \
        pool->free(mem, size_t);                                        \
    }                                                                   \
    void* operator new (size_t size, void* ptr, const char* file, int line) \
    {                                                                   \
        return ptr;                                                     \
    }                                                                   \
    void* operator new[] (size_t size, const char* file, int line)      \
    {                                                                   \
        return CALLOC(1,size);                                          \
    }                                                                   \
    void operator delete[] (void* mem, size_t size_t)                   \
    {                                                                   \
        FREE(mem);                                                      \
    }                                                                   \
    SET_SUPER_H(__quote__(ClassName),__quote__(SuperClass))             \

#define NEW_DELETE_OPERATORS_INLINE_CPP(ClassName,max)                  \
    Pool* ClassName::pool = NEW Pool(#ClassName,                        \
                                     sizeof(ClassName),                 \
                                     max,                               \
                                     0,                                 \
                                     false);                            \
    SET_SUPER_CPP(__quote__(ClassName))                                 \

#define NEW_DELETE_OPERATORS_TEMPLATE_CPP(ClassName,max)                \
    template <>                                                         \
    Pool* ClassName::pool = NEW Pool(#ClassName,                        \
                                     sizeof(ClassName),                 \
                                     max,                               \
                                     0,                                 \
                                     false);                            \
    template <>                                                         \
    SET_SUPER_CPP(__quote__(ClassName))                                 \

// http://www.codeguru.com/Cpp/Cpp/cpp_mfc/stl/article.php/c4079/

#include <algorithm>

template <typename T> class pool_allocator;

template <> class pool_allocator<void>
{
public:
    typedef void* pointer;
    typedef const void* const_pointer;
    // reference to void members are impossible.
    typedef void value_type;
    template <class U> 
        struct rebind { typedef pool_allocator<U> other; };
};    

namespace pool_alloc{
    inline void destruct (char *){}
    inline void destruct (wchar_t*){}
    template <typename T> 
        inline void destruct (T *t){t->~T();}
} // namespace

template <typename T>
class pool_allocator
{
public:
    typedef size_t size_type;
    typedef ptrdiff_t difference_type;
    typedef T* pointer;
    typedef const T* const_pointer;
    typedef T& reference;
    typedef const T& const_reference;
    typedef T value_type;

    template <class U> 
        struct rebind { typedef pool_allocator<U> other; };
    pool_allocator () {}
    pointer address (reference x) const {return &x;}
    const_pointer address (const_reference x) const {return &x;}
    pointer allocate (size_type size, pool_allocator<void>::const_pointer hint = 0)
    {
        return static_cast<pointer>(mem_->calloc(size*sizeof(T), __FILE__, __LINE__));
    }
    template <class U> pool_allocator (const pool_allocator<U>&){}
    void deallocate (pointer p, size_type n)
    {
        mem_->free(p, n*sizeof(T));
    }
    void deallocate (void *p, size_type n)
    {
        mem_->free(p, n*sizeof(T));
    }
    size_type max_size () const throw() {return size_t(-1) / sizeof(value_type);}
    void construct (pointer p, const T& val)
    {
        new(static_cast<void*>(p)) T(val);
    }
    void construct (pointer p)
    {
        new(static_cast<void*>(p)) T();
    }
    void destroy (pointer p){pool_alloc::destruct(p);}
private:
    
    static Pool* mem_;
};

template <typename T> Pool* pool_allocator<T>::mem_ = NEW Pool(typeid(T).name(), 
                                                               64,
                                                               10000,
                                                               0,
                                                               true);

template <typename T, typename U>
inline bool operator== (const pool_allocator<T>&, const pool_allocator<U>){return true;}

template <typename T, typename U>
inline bool operator!= (const pool_allocator<T>&, const pool_allocator<U>){return false;}

#endif // __POOL_H__
